Skip to content
lab components / Tables and lists

Table toolbar

A centralized location for managing and interacting with tabular data, offering functions like filtering, searching, exporting, and other table-related actions.

This is a Lab component!

That means it doesn't satisfy our definition of done and may be changed or even deleted. For an exact status, please reach out to the Fancy team through the dev_fancy or ux_fancy channels.

import { TableToolbar } from "@siteimprove/fancylab";

#Examples

#Usage with actions

Provide essential actions that operate on the entire table's data. Limit to two actions that directly support the table's main purpose.

Best Practices:

  • Place the most frequent or crucial actions first.
  • Use concise labels and relevant icons for easy identification.
  • Ideally, no more than two actions should be exposed as buttons. Use an Action Menu for additional actions.
<TableToolbar actions={ <> <Button variant="primary" onClick={() => console.log("some primary action")}> Primary </Button> <Button variant="secondary" onClick={() => console.log("some secondary action")}> Secondary </Button> </> } />

#Usage with filter

Allow users to refine table data based on specific criteria.

Best Practices:

  • Use a Filter Group to house multiple filters, maintaining a clean toolbar.
  • When filters are active, display filter pills (removable tags) directly on the toolbar to indicate the applied criteria.

Here is an example using a single filter that allow users to search for specific keywords (e.g country) within the table. Learn how to use filter groups in the guidelines for the Filters component.

type Country = { id: string; name: string }; const countries: Country[] = [ { id: "DK", name: "Denmark" }, { id: "SE", name: "Sweden" }, { id: "NO", name: "Norway" }, { id: "FI", name: "Finland" }, ]; const [country, setCountry] = useState<Country | undefined>(countries[0]); const onChange = (newValue: Country | undefined) => { console.log("Filter changed, calling API with new country", newValue); setCountry(newValue); }; const [filterButton, activeFilters] = useSingleFilter(country, onChange, { label: "Country", name: "country", stringify: (country) => country?.name, items: countries.map((country) => ({ title: country.name, value: country })), compareFn: (a, b) => a.id === b.id, }); return <TableToolbar filter={filterButton} activeFilters={activeFilters} />;

Enable quick location of items or information within the table.

Best Practices:

  • Instant Feedback: Provide real-time visual feedback (e.g., highlighting or filtering results) as users type.
  • Autocomplete (optional): Consider an Autocomplete component to suggest search terms.
const [searchValue, setSearchValue] = useState(""); const searchTypeItems = ["URL", "Title"]; const [selectedSearchType, setSelectedSearchType] = useState<string | undefined>( searchTypeItems[0] ); return ( <TableToolbar search={ <InputFieldWithSlug aria-label="Search the table" placeholder="Search" name="searchInput" value={searchValue} onChange={setSearchValue} leftSlug={ <Select items={searchTypeItems.map((x) => ({ value: x, title: x }))} value={selectedSearchType} onChange={setSelectedSearchType} hideClearButton aria-label="Search in URL or title" /> } rightSlug={ <Button onClick={() => console.log(searchValue)} aria-label="Submit search"> <Icon> <IconSearch /> </Icon> </Button> } /> } /> );

#Usage with custom views

Allow users to personalize the table's appearance or data display.

#Button

Opens a menu with predefined views or customization options (e.g., "Accessibility Issue" view).

return ( <TableToolbar customViews={ <Button> <Icon> <IconColumn /> </Icon> <InlineText>Custom views</InlineText> </Button> } /> );

#Toggle switch

Ideal for binary choices like "Compact" vs. "Detailed" view. (e.g. Performance > Page overview)

Best Practices:

  • Use descriptive labels for each view.
  • Provide a default view that suits most users.
const [toggleSwitchChecked, setToggleSwitchChecked] = useState<boolean>(false); return ( <TableToolbar customViews={ <ToggleSwitch label="Label" value={toggleSwitchChecked} onChange={setToggleSwitchChecked} /> } /> );

#Usage with exports

Enable users to download table data in various formats (CSV, Excel, etc.).

Learn how to use the normal exporter and the CSV exporter in the guidelines for the Table component.

Best Practices:

  • Clearly label the button as "Export" or use a recognizable download icon.
  • For multiple export formats (CSV, Excel, etc.), use an Action menu
return ( <TableToolbar exports={ <Button> <Icon> <IconDownload /> </Icon> <InlineText>Export</InlineText> </Button> } /> );

#Usage with actions, filter, search, custom views and exports

Combine all functionalities for comprehensive user control.

Best Practices:

  • Logical Grouping:
    • Group related functions (e.g., actions on the left, filtering/search/views/export on the right).
    • Use visual separators (divider or spacing) to distinguish groups.
  • Visual Hierarchy: Employ spacing, size, and position to indicate the relative importance of actions.
// Search const [searchValue, setSearchValue] = useState(""); const searchTypeItems = ["URL", "Title"]; const [selectedSearchType, setSelectedSearchType] = useState<string | undefined>( searchTypeItems[0] ); // Filters const countries = [ { id: "DK", name: "Denmark" }, { id: "SE", name: "Sweden" }, { id: "NO", name: "Norway" }, { id: "FI", name: "Finland" }, ]; const devices = [ { id: "desktop", name: "Desktop" }, { id: "mobile", name: "Mobile" }, { id: "tablet", name: "Tablet" }, { id: "laptop", name: "Laptop" }, ]; const [filters, setFilters] = useState({ country: countries[0], devices: [devices[0], devices[1]], }); const [filterGroup, activeFilters] = useFilterGroup(filters, setFilters, [ { label: "Country", name: "country", property: "country", stringify: ({ country }) => country?.name, items: countries.map((country) => ({ title: country.name, value: country })), compareFn: (a, b) => a.id === b.id, }, { label: "Devices", name: "devices", property: "devices", stringify: ({ devices }) => (devices || []).length > 0 ? devices?.map((b) => b.name).join(", ") : undefined, items: devices.map((device) => ({ title: device.name, value: device })), compareFn: (a, b) => a.id === b.id, }, ]); return ( <TableToolbar actions={ <> <Button variant="primary" onClick={() => console.log("some primary action")}> Primary </Button> <Button variant="secondary" onClick={() => console.log("some secondary action")}> Secondary </Button> </> } filter={filterGroup} activeFilters={activeFilters} search={ <InputFieldWithSlug aria-label="Search the table" placeholder="Search" name="searchInput" value={searchValue} onChange={setSearchValue} leftSlug={ <Select items={searchTypeItems.map((x) => ({ value: x, title: x }))} value={selectedSearchType} onChange={setSelectedSearchType} hideClearButton aria-label="Search in URL or title" /> } rightSlug={ <Button onClick={() => console.log(searchValue)} aria-label="Submit search"> <Icon> <IconSearch /> </Icon> </Button> } /> } customViews={ <Popover popoverContent={(id) => ( <div id={id}> <Content>Custom view content here</Content> <Popover.Footer> <ActionBar primary={{ children: "Confirm", onClick: console.log, }} cancel={{ children: "Cancel", onClick: console.log, }} /> </Popover.Footer> </div> )} buttonContent={ <> <Icon> <IconColumn /> </Icon> <InlineText>Custom views</InlineText> </> } hideChevron /> } exports={ <Button> <Icon> <IconDownload /> </Icon> <InlineText>Export</InlineText> </Button> } /> );

#Properties

PropertyDescriptionDefinedValue
actionsOptional
elementActions/Buttons to be displayed in the table toolbar
filterOptional
elementFilter to be displayed in the table toolbar
elementSearch to be displayed in the table toolbar
customViewsOptional
elementCustom views to be displayed in the table toolbar
exportsOptional
elementExport options to be displayed in the table toolbar
activeFiltersOptional
elementActive filters
data-observe-keyOptional
stringUnique string, used by external script e.g. for event tracking
classNameOptional
stringCustom className that's applied to the outermost element (only intended for special cases)
styleOptional
objectStyle object to apply custom inline styles (only intended for special cases)

#Guidelines

#Best practices

#General

Use TableToolbar to

  • Display large datasets. Help users manage and navigate extensive information.
  • Enable users to quickly refine data through filtering and search
  • Provide custom views to accommodate user preferences.

#Placement

TableToolbar is typically used in the following places:

  • Table /List table: Typically, the toolbar is placed directly above the table, making it easily accessible and visually associated with the table it controls
  • Side panel: / Dashboard widgets: the toolbar can be placed at the top of the panel or widgets containing the table. This helps maintain a consistent layout and keeps related controls together.

#Style

  • Siteimprove Design System: Adhere to Siteimprove's guidelines for color, typography, and spacing. If you are not using a component from Fancy, match the styling of your TableToolbar to existing components for visual consistency.
  • Focus on actions that operate on the entire table and directly support its primary purpose.
  • Use universally recognized icons .
  • The toolbar should adapt seamlessly to different screen sizes and devices.
  • Order of Toolbar Items: (Left to right) Primary Actions > Secondary Actions > Filter > Search > Custom Views > Export

#Do not use when

  • Inline actions are more suitable (e.g., editing individual cells).
  • The table has too many functionalities, consider alternative interaction patterns.

#Accessibility

#For designers

  • Ensure sufficient contrast, clear focus indicators, and descriptive labels/alt text for visual elements.

#For developers

This component comes with built-in accessibility, no extra work required.

Explore detailed guidelines for this component: Accessibility Specifications

#Writing

  • Ensure labels are concise and descriptive.
  • Avoid jargon and technical terms.
  • Use verbs for button labels (e.g., "Add," "Cancel," "Export") from the Word list